import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Scanner;
import java.util.TreeSet;


public class Asker {
	
	static QuestionData qData;
	
	static TreeSet<Integer> unaskedQuestions;
	static TreeSet<Integer> possibleAnswers;
	static ArrayList<Answer> change;
	/**
	 *    q q q
	 *  a * * +
	 *  a - + -
	 *  a * * *
	 */
	
	/**
	 * This method is called before the bot starts to ask questions,
	 * and might be used to set up datastructures etc.
	 * @throws IOException 
	 */
	private static void setup() throws IOException {
		
		qData=FileConvert.convertExcelToQData("ppl.xls");
		
		unaskedQuestions = new TreeSet<Integer>();
		possibleAnswers = new TreeSet<Integer>();
		
		change = new ArrayList<Asker.Answer>();
		
		for(int i = 0;i<qData.answers.size();i++){
			possibleAnswers.add(i);
		}
		for(int i = 0;i<qData.questions.size();i++){
			unaskedQuestions.add(i);
			change.add(Answer.u);
		}
		
		if(true){
			checkForClashes();
		}
	}
	
	private static void checkForClashes(){
		
		int failcount =0 ;
		
		for(int i = 0;i<qData.answers.size();i++){
			loop: for(int j = i+1;j<qData.answers.size();j++){
				for(int q=0;q<qData.questions.size();q++){
					if(areOpposites(qData.experience.get(i).get(q), qData.experience.get(j).get(q))){
						
						continue loop;
					}
				}
				failcount++;
				System.out.println("Clash: " + qData.answers.get(i) + "  == " + qData.answers.get(j));
			}
		}
		System.out.println("Failcount: " + failcount);
	}

	/**
	 * A simple enum to represent possible answers to our questions given by the user.
	 */
	public static enum Answer{
	    y, n, u
	}//yes,no,unknown
	
	private static boolean areOpposites(Answer a,Answer b){
		if(a.equals(Answer.y)&&b.equals(Answer.n))return true;
		if(a.equals(Answer.n)&&b.equals(Answer.y))return true;
		return false;
	}
	
	/**
	 * This method is called to decide whether the lowercase string ans 
	 * is a valid answer to one of our questions.
	 */
	private static boolean isValidAnswer(String ans) {
		String[] str = new String[]{"y","n","u"};
		for(String s:str){
			if (ans.equals(s))return true;
		}
		return false;
	}
	
	/**
	 * This method is called when the bot wonders what question to ask next.
	 */
	static int lastQuestion;
	private static String getQuestion(){
		int bestquestion = 0;
		int bestval      = -1;
		int locval       = 0;
		for(Integer q:unaskedQuestions){
			int y=0,n=0,u=0;
			for(Integer a:possibleAnswers){
				switch(qData.experience.get(a).get(q)){
				case y:y++;break;
				case n:n++;break;
				case u:u++;break;
				}
			}
			locval = Math.min(y, n);
			if(locval>bestval){
				bestval=locval;
				bestquestion=q;
			}
		}
		lastQuestion=bestquestion;
		unaskedQuestions.remove(bestquestion);
		return qData.questions.get(bestquestion);
	}
	
	/**
	 * This method is called after the bot has recieved a answer from our subject.
	 */
	private static void handleAnswer(Answer ans){
		change.set(lastQuestion, ans);
		HashSet<Integer> badAnswers = new HashSet<Integer>();
		if(!ans.equals(Answer.u)){
			for(Integer a:possibleAnswers){
				if(areOpposites(qData.experience.get(a).get(lastQuestion),ans)){
					badAnswers.add(a);
				}
			}
		}
		possibleAnswers.removeAll(badAnswers);
	}
	
	/**
	 * If this method returns true, we skip the rest of the questions and move right to 
	 * our guess.
	 */
	private static boolean wantToMakeGuess() {
		System.out.println(possibleAnswers.size()+" possible guesses left.");
		return (possibleAnswers.size()<=1);
	}
	
	/**
	 * This method is called when the bot needs to guess what the subject is thinking.
	 */
	
	static String lastGuess;
	private static void makeGuess(){
		if(possibleAnswers.size()>0){
			lastGuess=qData.answers.get(possibleAnswers.first());
			System.out.println("I believe you are thinking of " + lastGuess + ".");
		}
		else System.out.println("I have no idea what you are thinking of. :(");
	}
	
	/**
	 * This method is called when we know if our guess is correct or not.
	 */
	private static void handleGuessResponse(Answer answer) {
		//answer is _not_ unknown 
		
		String ans="z#xcv#bnmko#iuyt#rew#qaz";//A dummy string unlikley to ever be entered by a user.
		
		switch(answer){
		case y      : System.out.println("Lovely!" ) ;ans=lastGuess;break;
		case n      : System.out.println("Too bad. Who were you thinking of?");
                  	  Scanner sc = new Scanner(System.in);
                      ans = sc.nextLine();break;
		}
		boolean found = false;
        for (int i = 0; i < qData.answers.size(); i++) {
           if (ans.toLowerCase().equals(qData.answers.get(i).toLowerCase())) {
              found = true;
              for (int j = 0; j < qData.questions.size(); j++) {
                 if (!change.get(j).equals(qData.experience.get(i).get(j)) && change.get(j) != Answer.u) {
                    qData.experience.get(i).set(j, change.get(j));
                 }
              }
           } 
        } 
        if (found == false){
           qData.answers.add(NameCheck.upperCase(ans));
           qData.experience.add(change);
        }
	}
	
	/**
	 * This method is called after the game is finished, and might be used to save data 
	 * and other finalizing stuff.
	 */
	private static void doSomeFinalStuff() {
		System.out.println("Saving stuff.");
		try {
			FileConvert.convertQDataToExcel(qData, "ppl.xls");
		} catch (Exception e) {
			System.out.println("Failed to save stuff");
		}
	}

	private static void shutDown() {
		System.out.println("Shutting down");
	}
	
	public static void main(String arg[]){
		while(true){
			String ans;
			boolean firsttime;
			
			try{
				setup();
			}catch(Throwable t){
				System.out.println("an error occurred during setup: " + t.getMessage() );
			};
			
			Scanner sc = new Scanner(System.in);
			qloop:for(int i = 1;i<=20;i++){
				if(wantToMakeGuess()){
					break qloop;
				}
				String q = getQuestion();
				
				ans = "";
				firsttime = true;
				do{
					if(!firsttime)System.out.println("Bad input");
					System.out.println("["+i+"/"+20+"]: "+q+"   (y/n/u)");
					ans = sc.next().toLowerCase();
					firsttime = false;
				}while(!isValidAnswer(ans));
				handleAnswer(Answer.valueOf(ans));
			}
			makeGuess();
			
			firsttime = true;
			ans = "";
			
			do{
				if(!firsttime)System.out.println("Bad input");
				System.out.println("Is my guess correct? (y/n)");
				ans = sc.next().toLowerCase();
				firsttime = false;
			}while(!(ans.equals("y")||ans.equals("n")));
			
			handleGuessResponse(Answer.valueOf(ans));
			doSomeFinalStuff();
			

			firsttime = true;
			ans = "";
			
			do{
				if(!firsttime)System.out.println("Bad input");
				System.out.println("Do you want to play again? (y/n)");
				ans = sc.next().toLowerCase();
				firsttime = false;
			}while(!(ans.equals("y")||ans.equals("n")));
			
			if(ans.equals("n"))break;
		}
		shutDown();
	}
}
